|
1
|
|
|
const crawler = { |
|
2
|
|
|
|
|
3
|
|
|
que : [], |
|
4
|
|
|
tested : [], |
|
5
|
|
|
crawling : [], |
|
6
|
|
|
tests : [], |
|
7
|
|
|
ignore_paths : [], |
|
8
|
|
|
crawl_id : undefined, |
|
9
|
|
|
events : {}, |
|
10
|
|
|
linked_from : {}, |
|
11
|
|
|
useragent : 'desktop', |
|
12
|
|
|
|
|
13
|
|
|
/** |
|
14
|
|
|
* Register a test to run. |
|
15
|
|
|
* |
|
16
|
|
|
* @param {string} name |
|
17
|
|
|
* @param {string} title |
|
18
|
|
|
* @param {Array} headers |
|
19
|
|
|
* @param {string} callable |
|
20
|
|
|
* @returns {boolean} |
|
21
|
|
|
* @throws Exception |
|
22
|
|
|
*/ |
|
23
|
|
|
regiser_test: function(name, title, headers, callable){ |
|
24
|
|
|
if(name == undefined || this.get_test_by_name(name)) throw 'Invalid name specified for your test'; |
|
|
|
|
|
|
25
|
|
|
if(title == undefined) throw 'Title not specified'; |
|
|
|
|
|
|
26
|
|
|
if(!(headers instanceof Array) || headers.length < 1) throw 'Headers array is invalid'; |
|
|
|
|
|
|
27
|
|
|
if(typeof callable != 'function') return crawler_painter.create(name, title, headers); |
|
|
|
|
|
|
28
|
|
|
this.tests.push({name: name, title: title, callback: callable, cont:crawler_painter.create(name, title, headers)}); |
|
|
|
|
|
|
29
|
|
|
}, |
|
30
|
|
|
|
|
31
|
|
|
/** |
|
32
|
|
|
* Return a registered test by name |
|
33
|
|
|
* |
|
34
|
|
|
* @param {string} name |
|
35
|
|
|
* @returns {object|false} |
|
36
|
|
|
*/ |
|
37
|
|
|
get_test_by_name: function(name){ |
|
38
|
|
|
for(var t in this.test) if(this.tests[t]['name'] == name) return this.tests[t]; |
|
|
|
|
|
|
39
|
|
|
return false; |
|
40
|
|
|
}, |
|
41
|
|
|
|
|
42
|
|
|
/** |
|
43
|
|
|
* Check if the url passed is valid for crawling, if so and it hasn't |
|
44
|
|
|
* been added or crawled before, add it to the que |
|
45
|
|
|
* |
|
46
|
|
|
* Returns false if failed to add to que |
|
47
|
|
|
* |
|
48
|
|
|
* @param {string} url |
|
49
|
|
|
* @returns {boolean|undefined} |
|
50
|
|
|
*/ |
|
51
|
|
|
que_url: function(url){ |
|
52
|
|
|
var sanitized = this.sanitize(url); |
|
53
|
|
|
if( !this.can_crawl(url) || this.que.indexOf(sanitized) > -1 || !this.can_crawl(sanitized)) return false; |
|
|
|
|
|
|
54
|
|
|
this.que.push(sanitized); |
|
|
|
|
|
|
55
|
|
|
}, |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* Clean up a url so it becomes relative and standardized |
|
59
|
|
|
* |
|
60
|
|
|
* @param {string} url |
|
61
|
|
|
* @returns {string} |
|
62
|
|
|
*/ |
|
63
|
|
|
sanitize: function(url){ |
|
64
|
|
|
if(url == undefined) return ''; |
|
|
|
|
|
|
65
|
|
|
|
|
66
|
|
|
url = url |
|
67
|
|
|
.replace(/https?:\/\/[^\/]+/i, '') |
|
68
|
|
|
.replace(/^\/|\/$/g, '').split('#')[0]; |
|
69
|
|
|
|
|
70
|
|
|
if( url.slice(-1) == '?' ) url = url.slice(0, -1); |
|
|
|
|
|
|
71
|
|
|
if( url.length < 1 ) url = '/'; |
|
|
|
|
|
|
72
|
|
|
|
|
73
|
|
|
return url; |
|
74
|
|
|
}, |
|
75
|
|
|
|
|
76
|
|
|
/** |
|
77
|
|
|
* Get the domain for the passed url |
|
78
|
|
|
* |
|
79
|
|
|
* @param {string} url |
|
80
|
|
|
* @returns {string} |
|
81
|
|
|
*/ |
|
82
|
|
|
get_domain: function(url){ |
|
83
|
|
|
if( !url ) return ''; |
|
|
|
|
|
|
84
|
|
|
if( url.indexOf("://") > -1 ) return url.split('/')[2].split(':')[0]; |
|
|
|
|
|
|
85
|
|
|
else return url.split('/')[0].split(':')[0]; |
|
86
|
|
|
}, |
|
87
|
|
|
|
|
88
|
|
|
/** |
|
89
|
|
|
* Checks if the passed url should be ignored or not |
|
90
|
|
|
* |
|
91
|
|
|
* @param {string} url |
|
92
|
|
|
* @returns {boolean} |
|
93
|
|
|
*/ |
|
94
|
|
|
ignore_url: function( url ){ |
|
95
|
|
|
for(var regex in this.ignore_paths) { |
|
|
|
|
|
|
96
|
|
|
var reg = new RegExp(this.ignore_paths[regex], 'i'); |
|
97
|
|
|
if( url.match(reg) != null ) return true; |
|
|
|
|
|
|
98
|
|
|
} |
|
99
|
|
|
return false; |
|
100
|
|
|
}, |
|
101
|
|
|
|
|
102
|
|
|
/** |
|
103
|
|
|
* Add a path to ignore when crawler |
|
104
|
|
|
* Note: Paths can be in regex format |
|
105
|
|
|
* |
|
106
|
|
|
* @param {string} path |
|
107
|
|
|
* @returns {crawler} |
|
108
|
|
|
*/ |
|
109
|
|
|
add_ignore_path: function(path){ |
|
110
|
|
|
this.ignore_paths.push(path); |
|
111
|
|
|
return this; |
|
112
|
|
|
}, |
|
113
|
|
|
|
|
114
|
|
|
/** |
|
115
|
|
|
* Update all ignore paths to the paths specified |
|
116
|
|
|
* Note: Path can be in regex format |
|
117
|
|
|
* |
|
118
|
|
|
* @param paths |
|
119
|
|
|
* @returns {crawler} |
|
120
|
|
|
*/ |
|
121
|
|
|
set_ignore_paths: function(paths){ |
|
122
|
|
|
this.ignore_paths = paths; |
|
123
|
|
|
return this; |
|
124
|
|
|
}, |
|
125
|
|
|
|
|
126
|
|
|
/** |
|
127
|
|
|
* Sets the crawl id |
|
128
|
|
|
* |
|
129
|
|
|
* @param crawl_id |
|
130
|
|
|
* @returns {crawler} |
|
131
|
|
|
*/ |
|
132
|
|
|
set_crawl_id: function(crawl_id){ |
|
133
|
|
|
this.crawl_id = crawl_id; |
|
134
|
|
|
return this; |
|
135
|
|
|
}, |
|
136
|
|
|
|
|
137
|
|
|
/** |
|
138
|
|
|
* Does some soft checks to determine if url is a valid candidate for crawling |
|
139
|
|
|
* |
|
140
|
|
|
* @param {string} url |
|
141
|
|
|
* @returns {boolean} |
|
142
|
|
|
*/ |
|
143
|
|
|
can_crawl: function(url){ |
|
144
|
|
|
if(url == undefined) return false; |
|
|
|
|
|
|
145
|
|
|
return !(this.crawling.indexOf(url) >= 0 || this.tested.indexOf(url) >= 0 || |
|
146
|
|
|
this.is_file(url) || this.ignore_url(url) || this.is_external(url)); |
|
147
|
|
|
}, |
|
148
|
|
|
|
|
149
|
|
|
/** |
|
150
|
|
|
* Does a soft check for the url passed and checks if it's a file |
|
151
|
|
|
* by checking if it has an extension and if the extension contains 'html' |
|
152
|
|
|
* |
|
153
|
|
|
* @param {string} url |
|
154
|
|
|
* @returns {boolean} |
|
155
|
|
|
*/ |
|
156
|
|
|
is_file: function(url){ |
|
157
|
|
|
var split = this.sanitize( url ).split( '.' ); |
|
158
|
|
|
return split.length > 1 && split.pop().indexOf( 'html' ) < 0; |
|
159
|
|
|
}, |
|
160
|
|
|
|
|
161
|
|
|
/** |
|
162
|
|
|
* Does some soft checking for the url passed to see if it's external |
|
163
|
|
|
* Note: If the url is internal but redirects to an external source, we wown't detect it here |
|
164
|
|
|
* |
|
165
|
|
|
* @param {string} url |
|
166
|
|
|
* @returns {boolean} |
|
167
|
|
|
*/ |
|
168
|
|
|
is_external: function(url){ |
|
169
|
|
|
// Starts with / or # or doesn't have :// in it has to be internal |
|
170
|
|
|
if( url.length < 1 || url[0] == '/' || url[0] == '#' || url.indexOf('://') < 0 ) return false; |
|
|
|
|
|
|
171
|
|
|
|
|
172
|
|
|
// If we removed the domain and the url is still the same then it's an internal link without the leading / |
|
173
|
|
|
if( url == this.sanitize( url ) ) return false; |
|
|
|
|
|
|
174
|
|
|
|
|
175
|
|
|
// The domain is the same the domain we're running this script on |
|
176
|
|
|
if( this.get_domain( url ) == location.hostname ) return false; |
|
|
|
|
|
|
177
|
|
|
|
|
178
|
|
|
return true; |
|
179
|
|
|
}, |
|
180
|
|
|
|
|
181
|
|
|
/** |
|
182
|
|
|
* Checks if the href passed is an anchor link for url passed. |
|
183
|
|
|
* |
|
184
|
|
|
* @param {string} href |
|
185
|
|
|
* @param {string} url |
|
186
|
|
|
* @return {boolean} |
|
187
|
|
|
*/ |
|
188
|
|
|
is_anchor: function(href, url){ |
|
189
|
|
|
return href.indexOf('#') >= 0 && this.sanitize(href) == this.sanitize(url); |
|
190
|
|
|
}, |
|
191
|
|
|
|
|
192
|
|
|
/** |
|
193
|
|
|
* Fetch the next url from the que and run the tests on it |
|
194
|
|
|
*/ |
|
195
|
|
|
fetch_and_test: function(){ |
|
196
|
|
|
if( !this.que || this.que.length < 1 || this.que.length < 1 || $.active > 2 ) return false; |
|
|
|
|
|
|
197
|
|
|
|
|
198
|
|
|
var url = this.que.pop(); |
|
199
|
|
|
this.crawling.push(url); |
|
200
|
|
|
|
|
201
|
|
|
$.ajax({ |
|
202
|
|
|
url: this.get_proxy( url ), data: { agent: this.useragent }, accepts: 'json', dataType: 'json' |
|
203
|
|
|
}) |
|
204
|
|
|
.done(function( result ) { |
|
205
|
|
|
if(result['headers'] && result['body'] && result['body'].toLowerCase().indexOf('<head') >= 0) { |
|
206
|
|
|
if( !crawler.is_external(result['url_fetched']) ) { |
|
207
|
|
|
url = crawler.sanitize(result['url_fetched']); |
|
208
|
|
|
if(crawler.tested.indexOf(url) >= 0){ |
|
209
|
|
|
this.skipped = true; |
|
210
|
|
|
return true; |
|
211
|
|
|
} |
|
212
|
|
|
|
|
213
|
|
|
var html = $(crawler.strip_img_src(result['body'])); |
|
214
|
|
|
crawler.trigger('CRAWL_BEFORE_TESTS', [url]); |
|
215
|
|
|
crawler.fetch_links(html, url); |
|
216
|
|
|
crawler.run_tests(url, html, result['headers'], result['field_data'], result['phrases']); |
|
217
|
|
|
crawler.trigger('CRAWL_AFTER_TESTS', [url]); |
|
218
|
|
|
return true; |
|
219
|
|
|
} |
|
220
|
|
|
} |
|
221
|
|
|
crawler.trigger('CRAWL_LOAD_FAILED', [url]); |
|
|
|
|
|
|
222
|
|
|
}) |
|
223
|
|
|
.fail( function(){ crawler.trigger('CRAWL_LOAD_FAILED', [url]); }) |
|
224
|
|
|
.always( function(){ |
|
225
|
|
|
crawler.trigger('CRAWL_FINISHED', [url]); |
|
226
|
|
|
if((this.hasOwnProperty('skipped') && this.skipped) || crawler.tested.indexOf(url) < 0 ) |
|
227
|
|
|
crawler.tested.push(url) |
|
|
|
|
|
|
228
|
|
|
}); |
|
|
|
|
|
|
229
|
|
|
}, |
|
230
|
|
|
|
|
231
|
|
|
/** |
|
232
|
|
|
* Check for links in the html of the rendered page so we add them to the que |
|
233
|
|
|
* and also map how pages are linked to each other |
|
234
|
|
|
* |
|
235
|
|
|
* @param {jQuery} html |
|
236
|
|
|
* @param {string} url |
|
237
|
|
|
*/ |
|
238
|
|
|
fetch_links: function(html, url){ |
|
239
|
|
|
$.each(html.find('a'), function(){ |
|
240
|
|
|
var href = $(this).attr('href'), |
|
241
|
|
|
link = crawler.sanitize(href); |
|
242
|
|
|
|
|
243
|
|
|
crawler.que_url( href ); |
|
244
|
|
|
|
|
245
|
|
|
if(!crawler.linked_from.hasOwnProperty(link)) crawler.linked_from[link] = [url]; |
|
|
|
|
|
|
246
|
|
|
else if( crawler.linked_from[link].indexOf(url) < 0 ) crawler.linked_from[link].push(url); |
|
|
|
|
|
|
247
|
|
|
}); |
|
248
|
|
|
}, |
|
249
|
|
|
|
|
250
|
|
|
/** |
|
251
|
|
|
* Run the registered tests |
|
252
|
|
|
* |
|
253
|
|
|
* @param {string} url |
|
254
|
|
|
* @param {jQuery} html |
|
255
|
|
|
* @param {Array} headers |
|
256
|
|
|
* @param {Array} field_data |
|
257
|
|
|
* @param {Array} phrases |
|
258
|
|
|
*/ |
|
259
|
|
|
run_tests: function(url, html, headers, field_data, phrases){ |
|
260
|
|
|
for(var t in this.tests) { |
|
|
|
|
|
|
261
|
|
|
this.trigger('before'+this.tests[t]['name'], [url, html, headers, field_data, phrases]); |
|
262
|
|
|
this.tests[t]['callback'].apply(this.tests[t], [this.tests[t]['cont'], url, html, headers, field_data, phrases]); |
|
263
|
|
|
this.trigger('after'+this.tests[t]['name'], [url, html, headers, field_data, phrases]); |
|
264
|
|
|
} |
|
265
|
|
|
}, |
|
266
|
|
|
|
|
267
|
|
|
/** |
|
268
|
|
|
* Trigger event callback and pass on the data |
|
269
|
|
|
* |
|
270
|
|
|
* @param {string} event |
|
271
|
|
|
* @param {*} data |
|
272
|
|
|
*/ |
|
273
|
|
|
trigger: function(event, data){ |
|
274
|
|
|
if(this.events.hasOwnProperty(event)) |
|
275
|
|
|
for(var e in this.events[event]) this.events[event][e].apply(this, data); |
|
|
|
|
|
|
276
|
|
|
}, |
|
277
|
|
|
|
|
278
|
|
|
/** |
|
279
|
|
|
* Register callback on action |
|
280
|
|
|
* |
|
281
|
|
|
* @param {string} event |
|
282
|
|
|
* @param {function} callback |
|
283
|
|
|
* @returns {crawler} |
|
284
|
|
|
*/ |
|
285
|
|
|
on: function(event, callback){ |
|
286
|
|
|
if(!this.events.hasOwnProperty(event)) this.events[event] = []; |
|
|
|
|
|
|
287
|
|
|
this.events[event].push(callback); |
|
288
|
|
|
}, |
|
289
|
|
|
|
|
290
|
|
|
/** |
|
291
|
|
|
* Strip out src=<anything> so that we avoid loading the images |
|
292
|
|
|
* on the pages |
|
293
|
|
|
* |
|
294
|
|
|
* @param {string}html |
|
295
|
|
|
* @returns {string} |
|
296
|
|
|
*/ |
|
297
|
|
|
strip_img_src: function(html){ |
|
298
|
|
|
return html.replace( /(src).*?=(['|"].*?['|"])/ig, '' ); |
|
299
|
|
|
}, |
|
300
|
|
|
|
|
301
|
|
|
/** |
|
302
|
|
|
* Return the proxy url to test the passed url |
|
303
|
|
|
* |
|
304
|
|
|
* @param {$string} url |
|
305
|
|
|
* @returns {string} |
|
306
|
|
|
*/ |
|
307
|
|
|
get_proxy: function(url){ |
|
308
|
|
|
return location.protocol + '//' + location.hostname + '/seotest/getPageData?u='+url; |
|
309
|
|
|
}, |
|
310
|
|
|
|
|
311
|
|
|
/** |
|
312
|
|
|
* @see crawler_painter.add_row(name, data) |
|
313
|
|
|
* @param {string} name |
|
314
|
|
|
* @param {Array} data |
|
315
|
|
|
*/ |
|
316
|
|
|
add_row: function(name, data){ |
|
317
|
|
|
crawler_painter.add_row(name, data); |
|
|
|
|
|
|
318
|
|
|
}, |
|
319
|
|
|
|
|
320
|
|
|
/** |
|
321
|
|
|
* Returns the word count for a given set of sentences or string |
|
322
|
|
|
* |
|
323
|
|
|
* @param {string|array} data |
|
324
|
|
|
* @returns {number} |
|
325
|
|
|
*/ |
|
326
|
|
|
get_word_count: function(data){ |
|
327
|
|
|
if( typeof data === 'string' ) return data.split(' ').length; |
|
|
|
|
|
|
328
|
|
|
|
|
329
|
|
|
var count = 0; |
|
330
|
|
|
for( var d in data ) count += data[d].split(' ').length; |
|
|
|
|
|
|
331
|
|
|
return count; |
|
332
|
|
|
}, |
|
333
|
|
|
|
|
334
|
|
|
/** |
|
335
|
|
|
* Start the crawler |
|
336
|
|
|
* |
|
337
|
|
|
* @param {object} settings |
|
338
|
|
|
* @throws Exception |
|
339
|
|
|
*/ |
|
340
|
|
|
init: function(settings){ |
|
341
|
|
|
this.trigger('BEFORE_INIT', []); |
|
342
|
|
|
|
|
343
|
|
|
if(settings.hasOwnProperty('crawl_id')) this.set_crawl_id(settings['crawl_id']); |
|
|
|
|
|
|
344
|
|
|
if(settings.hasOwnProperty('ignore_paths')) this.set_ignore_paths(settings['ignore_paths']); |
|
|
|
|
|
|
345
|
|
|
|
|
346
|
|
|
if( !this.crawl_id ) throw "crawl_id must be specified"; |
|
|
|
|
|
|
347
|
|
|
|
|
348
|
|
|
// When a crawl finishes, start a new one if there are any more urls to go through else stop the auto-restart |
|
349
|
|
|
this.on('CRAWL_FINISHED', function(){ |
|
350
|
|
|
if( crawler.que.length > 0 ) crawler.fetch_and_test(); |
|
|
|
|
|
|
351
|
|
|
else window.clearInterval(crawler.interval); |
|
352
|
|
|
}); |
|
353
|
|
|
|
|
354
|
|
|
// Every second try to initialize a new crawl request just in-case something crashes |
|
355
|
|
|
this.interval = setInterval(function(){ crawler.fetch_and_test(); }, 1000); |
|
356
|
|
|
|
|
357
|
|
|
crawler_painter.init(); |
|
|
|
|
|
|
358
|
|
|
this.trigger('AFTER_INIT', []); |
|
359
|
|
|
} |
|
360
|
|
|
}; |
|
361
|
|
|
|
|
362
|
|
|
const crawler_painter = { |
|
363
|
|
|
|
|
364
|
|
|
containers: [], |
|
365
|
|
|
|
|
366
|
|
|
/** |
|
367
|
|
|
* Create a result table for the provided tests |
|
368
|
|
|
* |
|
369
|
|
|
* @param {string} name |
|
370
|
|
|
* @param {string} title |
|
371
|
|
|
* @param {Array} headers |
|
372
|
|
|
* @return {jQuery} |
|
373
|
|
|
*/ |
|
374
|
|
|
create: function(name, title, headers){ |
|
375
|
|
|
var container = $('<div class="infobox" id="'+name+'"></div>'), |
|
376
|
|
|
header = $('<div class="header clearfix"></div>'), |
|
377
|
|
|
count = $('<div class="count left"></div>'), |
|
378
|
|
|
toggle_button = $('<div class="icon toggle right closed"></div>').hide(), |
|
379
|
|
|
export_button = $('<div class="icon export right"></div>').hide(), |
|
380
|
|
|
title_bar = $('<h2 class="left">'+title+'</h2>'), |
|
381
|
|
|
table_cont = $('<div class="tableCont" style="display:none;"></div>'), |
|
382
|
|
|
thead = $('<tr></tr>'), |
|
383
|
|
|
table = $('<table class="table table-hover table-condensed"></table>').append(['<thead></thead>', '<tbody></tbody>']); |
|
384
|
|
|
|
|
385
|
|
|
for(var h in headers) thead.append('<th>'+headers[h]+'</th>'); |
|
|
|
|
|
|
386
|
|
|
table.find('thead').append(thead); |
|
387
|
|
|
|
|
388
|
|
|
header.append([count, title_bar, toggle_button, export_button]); |
|
389
|
|
|
table_cont.append(table); |
|
390
|
|
|
container.append([header, table_cont]); |
|
391
|
|
|
|
|
392
|
|
|
toggle_button.click(function(){ |
|
393
|
|
|
crawler.trigger('TOGGLED', [name]); |
|
394
|
|
|
var $this = $(this), |
|
395
|
|
|
css = ($this.hasClass('closed')) ? 'opened' : 'closed'; |
|
396
|
|
|
$this.parents('.infobox').find('.tableCont').slideToggle(); |
|
397
|
|
|
$this.removeClass('opened closed'); |
|
398
|
|
|
$this.addClass(css); |
|
399
|
|
|
}); |
|
400
|
|
|
|
|
401
|
|
|
export_button.click(function(){ |
|
402
|
|
|
crawler.trigger('BEFORE_EXPORT', [name]); |
|
403
|
|
|
var $this = $(this), |
|
404
|
|
|
rows = $this.parents( '.infobox' ).first().find( 'table tr' ), |
|
405
|
|
|
csvContent = "data:text/csv;charset=utf-8,"; |
|
406
|
|
|
|
|
407
|
|
|
$.each( rows, function(){ |
|
408
|
|
|
var item = []; |
|
409
|
|
|
$.each( $(this).find( 'th, td' ), function(){ item.push( $(this).text() ); }); |
|
410
|
|
|
csvContent += item.join(',') + "\n"; |
|
411
|
|
|
}); |
|
412
|
|
|
|
|
413
|
|
|
var link = document.createElement( 'a' ); |
|
414
|
|
|
link.setAttribute( 'href', encodeURI( csvContent ) ); |
|
415
|
|
|
link.setAttribute( 'download', name + '.csv' ); |
|
416
|
|
|
link.click(); |
|
417
|
|
|
crawler.trigger('AFTER_EXPORT', [name]); |
|
418
|
|
|
}); |
|
419
|
|
|
|
|
420
|
|
|
this.containers.push({'name': name, 'container': container}); |
|
421
|
|
|
return container; |
|
422
|
|
|
}, |
|
423
|
|
|
|
|
424
|
|
|
/** |
|
425
|
|
|
* Add a row of data to the container which matches |
|
426
|
|
|
* the name provided |
|
427
|
|
|
* |
|
428
|
|
|
* @param {string} name |
|
429
|
|
|
* @param {Array} data |
|
430
|
|
|
*/ |
|
431
|
|
|
add_row: function(name, data){ |
|
432
|
|
|
var cont = this.get_container_by_name(name), |
|
433
|
|
|
table = cont.find('tbody'), |
|
434
|
|
|
row = $('<tr></tr>').appendTo(table), |
|
435
|
|
|
len = table.find('tr').length; |
|
436
|
|
|
|
|
437
|
|
|
for(var d in data) row.append($('<td/>').append(data[d])); |
|
|
|
|
|
|
438
|
|
|
|
|
439
|
|
|
// Show icons if we have items |
|
440
|
|
|
if( len > 0 ){ |
|
441
|
|
|
cont.find('.icon.export').fadeIn(); |
|
442
|
|
|
cont.find('.icon.toggle').fadeIn(); |
|
443
|
|
|
} |
|
444
|
|
|
|
|
445
|
|
|
cont.find('.count').html(len); |
|
446
|
|
|
|
|
447
|
|
|
// Set the header colour |
|
448
|
|
|
if( cont.find('td div.alert-danger').length > 0 ) crawler_painter.set_type(name, 'error'); |
|
|
|
|
|
|
449
|
|
|
else if( cont.find('td div.alert-warning').length > 0 ) crawler_painter.set_type(name, 'warning'); |
|
|
|
|
|
|
450
|
|
|
else if( cont.find('td div.alert-info').length > 0 ) crawler_painter.set_type(name, 'info'); |
|
|
|
|
|
|
451
|
|
|
else if( cont.find('td div.alert-success').length > 0 ) crawler_painter.set_type(name, 'success'); |
|
|
|
|
|
|
452
|
|
|
}, |
|
453
|
|
|
|
|
454
|
|
|
/** |
|
455
|
|
|
* Reset the data inside of the table for the container named {name} |
|
456
|
|
|
* |
|
457
|
|
|
* @param {string} name |
|
458
|
|
|
* @param {string|undefined} type |
|
459
|
|
|
*/ |
|
460
|
|
|
reset_table: function(name, type){ |
|
461
|
|
|
var cont = this.get_container_by_name(name); |
|
462
|
|
|
|
|
463
|
|
|
cont.find('tbody tr').remove(); |
|
464
|
|
|
cont.find('.count').html(''); |
|
465
|
|
|
cont.find('.icon.export').hide(); |
|
466
|
|
|
cont.find('.icon.toggle').hide(); |
|
467
|
|
|
|
|
468
|
|
|
if( type != undefined ) this.set_type(name, type); |
|
|
|
|
|
|
469
|
|
|
}, |
|
470
|
|
|
|
|
471
|
|
|
/** |
|
472
|
|
|
* Create a status field to be used in the report rows |
|
473
|
|
|
* |
|
474
|
|
|
* @param string type |
|
|
|
|
|
|
475
|
|
|
* @param string text |
|
|
|
|
|
|
476
|
|
|
*/ |
|
477
|
|
|
create_status: function(type, text){ |
|
478
|
|
|
var ret = $('<div class="status-text alert"></div>'); |
|
479
|
|
|
switch(type){ |
|
|
|
|
|
|
480
|
|
|
case 'info': |
|
481
|
|
|
ret.addClass('alert-info'); |
|
482
|
|
|
ret.append('<i class="glyphicon glyphicon-info-sign"> </i>'); |
|
483
|
|
|
break; |
|
484
|
|
|
|
|
485
|
|
|
case 'error': |
|
486
|
|
|
ret.addClass('alert-danger'); |
|
487
|
|
|
ret.append('<i class="glyphicon glyphicon-exclamation-sign"> </i>'); |
|
488
|
|
|
break; |
|
489
|
|
|
|
|
490
|
|
|
case 'success': |
|
491
|
|
|
ret.addClass('alert-success'); |
|
492
|
|
|
ret.append('<i class="glyphicon glyphicon-ok-sign"> </i>'); |
|
493
|
|
|
break; |
|
494
|
|
|
|
|
495
|
|
|
case 'warning': |
|
496
|
|
|
ret.addClass('alert-warning'); |
|
497
|
|
|
ret.append('<i class="glyphicon glyphicon-warning-sign"> </i>'); |
|
498
|
|
|
break; |
|
499
|
|
|
} |
|
500
|
|
|
ret.append(text); |
|
501
|
|
|
return ret; |
|
502
|
|
|
}, |
|
503
|
|
|
|
|
504
|
|
|
/** |
|
505
|
|
|
* Return the container matching the provided name |
|
506
|
|
|
* |
|
507
|
|
|
* @param {string} name |
|
508
|
|
|
* @returns {jQuery} |
|
509
|
|
|
*/ |
|
510
|
|
|
get_container_by_name: function(name){ |
|
511
|
|
|
for(var c in this.containers) if(this.containers[c]['name'] == name) return this.containers[c]['container']; |
|
|
|
|
|
|
512
|
|
|
}, |
|
513
|
|
|
|
|
514
|
|
|
/** |
|
515
|
|
|
* Set the type of the test so it's colour changes according |
|
516
|
|
|
* |
|
517
|
|
|
* @param {string} name |
|
518
|
|
|
* @param {string} type |
|
519
|
|
|
*/ |
|
520
|
|
|
set_type: function(name, type){ |
|
521
|
|
|
var cont = this.get_container_by_name(name); |
|
522
|
|
|
cont.removeClass('blue red green yellow purple'); |
|
523
|
|
|
switch(type){ |
|
524
|
|
|
case 'info': return cont.addClass('blue'); |
|
525
|
|
|
case 'error': return cont.addClass('red'); |
|
526
|
|
|
case 'success': return cont.addClass('green'); |
|
527
|
|
|
case 'warning': return cont.addClass('yellow'); |
|
528
|
|
|
default: return cont.addClass('purple'); |
|
529
|
|
|
} |
|
530
|
|
|
}, |
|
531
|
|
|
|
|
532
|
|
|
/** |
|
533
|
|
|
* Update the header stats |
|
534
|
|
|
*/ |
|
535
|
|
|
update_header: function(){ |
|
536
|
|
|
$('#leftcount').html(crawler.que.length); |
|
537
|
|
|
$('#donecount').html(crawler.tested.length); |
|
538
|
|
|
|
|
539
|
|
|
if(crawler.que.length > 0 ) $('#analyzestatus').html('Analyzing'); |
|
|
|
|
|
|
540
|
|
|
else if(crawler.que.length < 1 && crawler.tested.length > 0) $('#analyzestatus').html('Finished'); |
|
|
|
|
|
|
541
|
|
|
}, |
|
542
|
|
|
|
|
543
|
|
|
/** |
|
544
|
|
|
* Returns a link out of the passed url |
|
545
|
|
|
* |
|
546
|
|
|
* @param {string} url |
|
547
|
|
|
* @returns {string} |
|
548
|
|
|
*/ |
|
549
|
|
|
create_link: function(url, anchor){ |
|
550
|
|
|
anchor = (anchor) ? anchor : url; |
|
551
|
|
|
return '<a class="btn btn-link" href="'+url+'" title="'+anchor+'" target="_blank" rel="nofollow">' |
|
552
|
|
|
+'<span class="glyphicon glyphicon-new-window"> </span>'+ |
|
553
|
|
|
((anchor.length > 29) ? anchor.substr(0, 27) + '...' : anchor) |
|
554
|
|
|
+'</a>'; |
|
555
|
|
|
}, |
|
556
|
|
|
|
|
557
|
|
|
/** |
|
558
|
|
|
* Initialize the painter |
|
559
|
|
|
*/ |
|
560
|
|
|
init:function(){ |
|
561
|
|
|
for(var c in this.containers) $('#results_container').append(this.containers[c]['container']); |
|
|
|
|
|
|
562
|
|
|
crawler.on('CRAWL_FINISHED', this.update_header); |
|
563
|
|
|
} |
|
564
|
|
|
}; |
|
565
|
|
|
|
Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.
Consider:
If you or someone else later decides to put another statement in, only the first statement will be executed.
In this case the statement
b = 42will always be executed, while the logging statement will be executed conditionally.ensures that the proper code will be executed conditionally no matter how many statements are added or removed.